22025
9251
Boeken in programmeertaal leggen uit dat waardetypen op de stapel worden gemaakt en referentietypen op de stapel worden gemaakt, zonder uit te leggen wat deze twee dingen zijn. Ik heb hier geen duidelijke uitleg over gelezen. Ik begrijp wat een stapel is. Maar,
Waar en wat zijn ze (fysiek in het geheugen van een echte computer)?
In hoeverre worden ze gecontroleerd door het besturingssysteem of de taalrun-time?
Wat is hun reikwijdte?
Wat bepaalt de grootte van elk van hen?
Wat maakt iemand sneller? 
De stapel is het geheugen dat gereserveerd is als krasruimte voor een uitvoeringsdraad. Wanneer een functie wordt aangeroepen, wordt bovenaan de stapel een blok gereserveerd voor lokale variabelen en enkele boekhoudgegevens. Wanneer die functie terugkeert, wordt het blok ongebruikt en kan het de volgende keer dat een functie wordt aangeroepen worden gebruikt. De stapel wordt altijd gereserveerd in een LIFO-volgorde (last in first out); het meest recent gereserveerde blok is altijd het volgende blok dat moet worden vrijgemaakt. Dit maakt het heel eenvoudig om de stapel bij te houden; het vrijmaken van een blok van de stapel is niets meer dan het aanpassen van één aanwijzer.
De heap is geheugen gereserveerd voor dynamische toewijzing. In tegenstelling tot de stapel, is er geen afgedwongen patroon voor het toewijzen en ongedaan maken van de toewijzing van blokken van de heap; u kunt op elk moment een blok toewijzen en op elk moment vrijmaken. Dit maakt het veel complexer om op een bepaald moment bij te houden welke delen van de heap zijn toegewezen of vrij zijn; er zijn veel aangepaste heap-allocators beschikbaar om de heap-prestaties af te stemmen op verschillende gebruikspatronen.
Elke thread krijgt een stapel, terwijl er meestal maar één heap is voor de applicatie (hoewel het niet ongebruikelijk is om meerdere hopen te hebben voor verschillende soorten toewijzingen).
Om uw vragen direct te beantwoorden:
In hoeverre worden ze gecontroleerd door het besturingssysteem of de taalruntime?
Het besturingssysteem wijst de stapel toe voor elke thread op systeemniveau wanneer de thread wordt gemaakt. Meestal wordt het besturingssysteem aangeroepen door de taalruntime om de heap voor de toepassing toe te wijzen.
Wat is hun reikwijdte?
De stapel is vastgemaakt aan een draad, dus wanneer de draad eruit komt, wordt de stapel teruggewonnen. De heap wordt doorgaans toegewezen tijdens het opstarten van de applicatie door de runtime, en wordt teruggewonnen wanneer de applicatie (technisch proces) wordt afgesloten.
Wat bepaalt de grootte van elk van hen?
De grootte van de stapel wordt ingesteld wanneer een thread wordt gemaakt. De grootte van de heap wordt ingesteld bij het opstarten van de toepassing, maar kan toenemen naarmate er ruimte nodig is (de allocator vraagt ​​meer geheugen van het besturingssysteem).
Wat maakt iemand sneller?
De stapel is sneller omdat het toegangspatroon het triviaal maakt om er geheugen aan toe te wijzen en de toewijzing ervan ongedaan te maken (een aanwijzer / geheel getal wordt eenvoudigweg verhoogd of verlaagd), terwijl de heap een veel complexere boekhouding heeft die betrokken is bij een toewijzing of ongedaan maken van de toewijzing. Bovendien wordt elke byte in de stack zeer vaak hergebruikt, wat betekent dat deze de neiging heeft om te worden toegewezen aan de cache van de processor, waardoor deze erg snel is. Een andere prestatiehit voor de heap is dat de heap, die meestal een globale bron is, typisch multi-threading veilig moet zijn, d.w.z. elke toewijzing en deallocatie moet - typisch - worden gesynchroniseerd met "alle" andere heap-toegangen in het programma.
Een duidelijke demonstratie:
Afbeeldingsbron: vikashazrati.wordpress.com
|
Stapel:
Net als de hoop opgeslagen in computer-RAM.
Variabelen die op de stapel zijn gemaakt, vallen buiten het bereik en worden automatisch ongedaan gemaakt.
Veel sneller toe te wijzen in vergelijking met variabelen op de heap.
Geïmplementeerd met een daadwerkelijke stack-datastructuur.
Slaat lokale gegevens op, retouradressen, die worden gebruikt voor het doorgeven van parameters.
Kan een stack overflow hebben als er te veel van de stack wordt gebruikt (meestal door oneindige of te diepe recursie, zeer grote allocaties).
Gegevens die op de stapel zijn gemaakt, kunnen zonder verwijzingen worden gebruikt.
U zou de stapel gebruiken als u precies weet hoeveel gegevens u moet toewijzen voordat u compileert en deze niet te groot is.
Meestal is er al een maximale grootte bepaald wanneer je programma start.
Hoop:
Net als de stapel opgeslagen in computer-RAM.
In C ++ moeten variabelen op de heap handmatig worden vernietigd en vallen ze nooit buiten het bereik. De gegevens worden vrijgegeven met verwijderen, verwijderen [] of gratis.
Langzamer toe te wijzen in vergelijking met variabelen op de stapel.
Wordt op aanvraag gebruikt om een ​​gegevensblok toe te wijzen voor gebruik door het programma.
Kan fragmentatie hebben als er veel toewijzingen en deallocaties zijn.
In C ++ of C wordt naar gegevens die op de heap zijn gemaakt, verwezen door aanwijzers en respectievelijk toegewezen aan nieuw of malloc.
Kan toewijzingsfouten hebben als een te grote buffer wordt gevraagd om te worden toegewezen.
U zou de heap gebruiken als u niet precies weet hoeveel gegevens u tijdens runtime nodig heeft of als u veel gegevens moet toewijzen.
Verantwoordelijk voor geheugenlekken.
Voorbeeld:
int foo ()
{
char * pBuffer; // <- nog niets toegewezen (met uitzondering van de pointer zelf, die hier op de stapel wordt toegewezen).
bool b = waar; // Toegekend op de stapel.
als (b)
{
// Maak 500 bytes op de stapel
char buffer [500];
// Maak 500 bytes op de heap
pBuffer = nieuwe char [500];
} // <- buffer is hier opgeheven, pBuffer niet
} // <--- oeps, er is een geheugenlek. Ik had delete [] pBuffer moeten bellen;
|
Het belangrijkste punt is dat heap en stack algemene termen zijn voor manieren waarop geheugen kan worden toegewezen. Ze kunnen op veel verschillende manieren worden geïmplementeerd en de termen zijn van toepassing op de basisconcepten.
In een stapel items zitten items op elkaar in de volgorde waarin ze daar zijn geplaatst, en je kunt alleen de bovenste verwijderen(zonder het hele ding omver te werpen).
De eenvoud van een stapel is dat u geen tabel hoeft bij te houden met een record van elke sectie van het toegewezen geheugen; de enige statusinformatie die u nodig heeft, is een enkele wijzer naar het einde van de stapel. Om toe te wijzen en opnieuw toe te wijzen, verhoogt en verlaagt u gewoon die enkele aanwijzer. Opmerking: een stapel kan soms worden geïmplementeerd om bovenaan een gedeelte van het geheugen te beginnen en zich naar beneden toe uit te breiden in plaats van naar boven te groeien.
In een hoop is er geen specifieke volgorde in de manier waarop items worden geplaatst. U kunt items in elke volgorde bereiken en verwijderen omdat er geen duidelijk 'top'-item is.
Heap-toewijzing vereist het bijhouden van een volledig record van welk geheugen is toegewezen en wat niet, evenals wat overheadonderhoud om fragmentatie te verminderen, aaneengesloten geheugensegmenten te vinden die groot genoeg zijn om in de gevraagde grootte te passen, enzovoort. Het geheugen kan op elk moment worden vrijgemaakt, waardoor er vrije ruimte overblijft. Soms voert een geheugentoewijzing onderhoudstaken uit, zoals het defragmenteren van geheugen door toegewezen geheugen te verplaatsen of garbagecollection - tijdens runtime identificeren wanneer geheugen niet langer in bereik is en de toewijzing ongedaan maken.
Deze afbeeldingen zouden redelijk goed de twee manieren moeten beschrijven van het toewijzen en vrijmaken van geheugen in een stapel en een hoop. Yum!
In hoeverre worden ze gecontroleerd door het besturingssysteem of de taalruntime?
Zoals vermeld, zijn heap en stack algemene termen en kunnen ze op veel manieren worden geïmplementeerd. Computerprogramma's hebben doorgaans een stapel die een oproepstapel wordt genoemd en die informatie opslaat die relevant is voor de huidige functie, zoals een aanwijzer naar de functie waaruit deze is aangeroepen en eventuele lokale variabelen. Omdat functies andere functies aanroepen en vervolgens terugkeren, wordt de stapel groter en kleiner om informatie van de functies verderop in de oproepstapel vast te houden. Een programma heeft er niet echt runtime-controle over; het wordt bepaald door de programmeertaal, het besturingssysteem en zelfs de systeemarchitectuur.
Een heap is een algemene term die wordt gebruikt voor elk geheugen dat dynamisch en willekeurig wordt toegewezen; d.w.z. buiten gebruik. Het geheugen wordt doorgaans toegewezen door het besturingssysteem, waarbij de applicatie API-functies aanroept om deze toewijzing te doen. Er is nogal wat overhead vereist bij het beheren van dynamisch toegewezen geheugen, dat meestal wordt afgehandeld door de runtime-code van de gebruikte programmeertaal of omgeving.
Wat is hun reikwijdte?
De call-stack is zo'n laag niveau concept dat het geen verband houdt met 'scope' in de zin van programmeren. Als je een code uit elkaar haalt, zie je relatieve pointerstijlverwijzingen naar delen van de stapel, maar voor zover het een taal van een hoger niveau betreft, legt de taal zijn eigen regels op. Een belangrijk aspect van een stapel is echter dat zodra een functie terugkeert, alles wat lokaal is voor die functie onmiddellijk van de stapel wordt bevrijd. Dat werkt zoals je zou verwachten, gezien de manier waarop je programmeertalen werken. Op een hoop is het ook moeilijk te definiëren. De scope is datgene wat door het besturingssysteem wordt getoond, maar uw programmeertaal voegt waarschijnlijk regels toe over wat een "scope" is in uw applicatie. De processorarchitectuur en het besturingssysteem gebruiken virtuele adressering, die de processor vertaalt naar fysieke adressen en er zijn paginafouten, enz. Ze houden bij welke pagina's bij welke applicaties horen. U hoeft zich hier echter nooit echt zorgen over te maken, omdat u gewoon de methode gebruikt die uw programmeertaal gebruikt om geheugen toe te wijzen en vrij te maken, en controleert op fouten (als het toewijzen / vrijgeven om welke reden dan ook mislukt).
Wat bepaalt de grootte van elk van hen?
Nogmaals, het hangt af van de taal, compiler, besturingssysteem en architectuur. Een stack is meestal vooraf toegewezen, omdat het per definitie aaneengesloten geheugen moet zijn. De taalcompiler of het besturingssysteem bepalen de grootte. U slaat geen enorme hoeveelheden gegevens op de stapel op, dus het zal groot genoeg zijn om nooit volledig te worden gebruikt, behalve in gevallen van ongewenste eindeloze recursie (vandaar "stack-overflow") of andere ongebruikelijke programmeerbeslissingen.
Een heap is een algemene term voor alles dat dynamisch kan worden toegewezen. Afhankelijk van hoe je ernaar kijkt, verandert het voortdurend van formaat. In moderne processors en besturingssystemen is de exacte manier waarop het werkt sowieso erg geabstraheerd, dus u hoeft zich normaal gesproken niet veel zorgen te maken over hoe het diep van binnen werkt, behalve dat u (in talen waarin het u toelaat) geen geheugen mag gebruiken dat je hebt nog geen geheugen toegewezen dat je hebt vrijgemaakt.
Wat maakt iemand sneller?
De stapel is sneller omdat al het vrije geheugen altijd aaneengesloten is. Er hoeft geen lijst te worden bijgehouden van alle segmenten van vrij geheugen, slechts een enkele wijzer naar de huidige bovenkant van de stapel. Hiervoor bewaren compilers deze pointer meestal in een speciaal, snel register. Bovendien zijn volgende bewerkingen op een stapel meestal geconcentreerd in zeer nabije geheugengebieden, wat op een zeer laag niveau goed is voor optimalisatie door de on-die processor.caches.
|
(Ik heb dit antwoord verplaatst van een andere vraag die min of meer een dupe was van deze.)
Het antwoord op uw vraag is implementatiespecifiek en kan variëren tussen compilers en processorarchitecturen. Hier is echter een vereenvoudigde uitleg.
Zowel de stack als de heap zijn geheugengebieden die zijn toegewezen vanuit het onderliggende besturingssysteem (vaak virtueel geheugen dat op aanvraag wordt toegewezen aan fysiek geheugen).
In een omgeving met meerdere threads heeft elke thread zijn eigen volledig onafhankelijke stapel, maar ze delen de heap. Gelijktijdige toegang moet op de heap worden gecontroleerd en is niet mogelijk op de stapel.
De hoop
De heap bevat een gekoppelde lijst met gebruikte en vrije blokken. Aan nieuwe toewijzingen op de heap (door nieuw of malloc) wordt voldaan door een geschikt blok te maken uit een van de vrije blokken. Dit vereist een update van de lijst met blokken op de heap. Deze meta-informatie over de blokken op de heap wordt ook op de heap opgeslagen, vaak in een klein gebied vlak voor elk blok.
Naarmate de heap groeit, worden er vaak nieuwe blokken toegewezen van lagere adressen naar hogere adressen. U kunt de hoop dus zien als een hoop geheugenblokken die in omvang toenemen naarmate geheugen wordt toegewezen. Als de heap te klein is voor een toewijzing, kan de grootte vaak worden vergroot door meer geheugen van het onderliggende besturingssysteem te verkrijgen.
Het toewijzen en ongedaan maken van de toewijzing van veel kleine blokken kan de heap in een staat achterlaten waarin er veel kleine vrije blokken tussen de gebruikte blokken zijn. Een verzoek om een ​​groot blok toe te wijzen kan mislukken omdat geen van de vrije blokken groot genoeg is om aan het toewijzingsverzoek te voldoen, ook al is de gecombineerde grootte van de vrije blokken groot genoeg. Dit wordt heapfragmentatie genoemd.
Wanneer een gebruikt blok dat grenst aan een vrij blok wordt ongedaan gemaakt, kan het nieuwe vrije blok worden samengevoegd met het aangrenzende vrije blok om een ​​groter vrij blok te creëren, waardoor de fragmentatie van de hoop effectief wordt verminderd.
De stapel
De stapel werkt vaak nauw samen met een speciaal register op de CPU dat de stapelaanwijzer wordt genoemd. Aanvankelijk wijst de stapelaanwijzer naar de bovenkant van de stapel (het hoogste adres op de stapel).
De CPU heeft speciale instructies om waarden op de stapel te duwen en ze weer uit de stapel te halen. Elke keer drukken slaat de waarde op de huidige locatie van de stapelaanwijzer op en verkleint de stapelaanwijzer. Een pop haalt de waarde op waarnaar wordt verwezen door de stapelaanwijzer en verhoogt vervolgens de stapelaanwijzer (wees niet verward door het feit dat het toevoegen van een waarde aan de stapel de stapelaanwijzer verlaagt en het verwijderen van een waarde deze verhoogt. Onthoud dat de stapel groeit naar de onderkant). De waarden die worden opgeslagen en opgehaald, zijn de waarden van de CPU-registers.
Wanneer een functie wordt aangeroepen, gebruikt de CPU speciale instructies die op de huidige instructiepointer drukken, d.w.z. het adres van de code die op de stapel wordt uitgevoerd. De CPU springt vervolgens naar de functie door de
instructie pointer naar het adres van de aangeroepen functie. Later, wanneer de functie terugkeert, wordt de oude instructie-pointer uit de stapel gehaald en wordt de uitvoering hervat bij de code net na het aanroepen van de functie.
Wanneer een functie wordt ingevoerd, wordt de stapelaanwijzer verkleind om meer ruimte op de stapel toe te wijzen voor lokale (automatische) variabelen. Als de functie één lokale 32-bits variabele heeft, worden vier bytes op de stapel gereserveerd. Wanneer de functie terugkeert, wordt de stapelaanwijzer terug verplaatst om het toegewezen gebied vrij te maken.
Als een functie parameters heeft, worden deze op de stapel geduwd voordat de functie wordt aangeroepen. De code in de functie kan vervolgens vanaf de huidige stapelaanwijzer omhoog in de stapel navigeren om deze waarden te lokaliseren.
Het nesten van functieaanroepen werkt als een zonnetje. Elke nieuwe oproep wijst functieparameters, het retouradres en ruimte voor lokale variabelen toe en deze activeringsrecords kunnen worden gestapeld voor geneste oproepen en zullen op de juiste manier worden afgewikkeld wanneer de functies terugkeren.
Aangezien de stack een beperkt geheugenblok is, kunt u een stack-overflow veroorzaken door te veel geneste functies aan te roepen en / of te veel ruimte toe te wijzen aan lokale variabelen. Vaak is het geheugengebied dat wordt gebruikt voor de stapel zo ingesteld dat schrijven onder de onderkant (het laagste adres) van de stapel een val of uitzondering in de CPU activeert. Deze uitzonderlijke toestand kan vervolgens worden opgevangen door de runtime en omgezet in een soort uitzondering voor stack-overflow.
Kan een functie op de heap worden toegewezen in plaats van een stapel?
Nee, activeringsrecords voor functies (d.w.z. lokale of automatische variabelen) worden toegewezen op de stapel die niet alleen wordt gebruikt om deze variabelen op te slaan, maar ook om geneste functieaanroepen bij te houden.
Hoe de heap wordt beheerd, hangt echt af van de runtime-omgeving. C gebruikt malloc en C ++ gebruikt nieuw, maar veel andere talen hebben garbage collection.
De stapel is echter een functie op een lager niveau die nauw verband houdt met de processorarchitectuur. De hoop laten groeien als er niet genoeg ruimte is, is sindsdien niet zo moeilijkhet kan worden geïmplementeerd in de bibliotheekoproep die de heap afhandelt. Het laten groeien van de stack is echter vaak onmogelijk omdat de stack-overflow pas wordt ontdekt als het te laat is; en het afsluiten van de thread van uitvoering is de enige haalbare optie.
|
In de volgende C # -code
openbare leegte Method1 ()
{
int i = 4;
int y = 2;
class1 cls1 = nieuwe class1 ();
}
Hier is hoe het geheugen wordt beheerd
Lokale variabelen die alleen nodig zijn zolang de functie-aanroep in de stapel gaat. De heap wordt gebruikt voor variabelen waarvan we de levensduur niet echt kennen, maar waarvan we verwachten dat ze een tijdje zullen duren. In de meeste talen is het essentieel dat we tijdens het compileren weten hoe groot een variabele is als we deze op de stapel willen opslaan.
Objecten (die in grootte variëren naarmate we ze bijwerken) gaan op de hoop omdat we bij het maken niet weten hoe lang ze meegaan. In veel talen is de heap garbage verzameld om objecten te vinden (zoals het cls1-object) die geen verwijzingen meer hebben.
In Java gaan de meeste objecten rechtstreeks naar de heap. In talen als C / C ++ kunnen structs en klassen vaak op de stapel blijven staan ​​als je niet met pointers te maken hebt.
Meer informatie vind je hier:
Het verschil tussen stack- en heapgeheugentoewijzing «timmurphy.org
en hier:
Objecten maken op de stapel en hoop
Dit artikel is de bron van bovenstaande afbeelding: zes belangrijke .NET-concepten: stack, heap, waardetypen, referentietypes, boksen en unboxen - CodeProject
maar houd er rekening mee dat het enkele onnauwkeurigheden kan bevatten.
|
De stapel
Als je een functie aanroept, worden de argumenten voor die functie plus wat andere overhead op de stapel gezet. Enige info (zoals waar je heen moet bij terugkomst) wordt daar ook opgeslagen.
Wanneer u een variabele binnen uw functie declareert, wordt die variabele ook op de stapel toegewezen.
De toewijzing van de stapel is vrij eenvoudig omdat u de toewijzing altijd in omgekeerde volgorde doet. Stack-items worden toegevoegd als u functies invoert, de bijbehorende gegevens worden verwijderd als u ze afsluit. Dit betekent dat je de neiging hebt om binnen een klein gebied van de stapel te blijven, tenzij je veel functies aanroept die veel andere functies aanroepen (of een recursieve oplossing maakt).
De hoop
De heap is een algemene naam voor de plaats waar u de gegevens plaatst die u on-the-fly maakt. Als je niet weet hoeveel ruimteschepen je programma gaat maken, gebruik je waarschijnlijk de nieuwe (of malloc of gelijkwaardige) operator om elk ruimteschip te creëren. Deze toewijzing blijft een tijdje hangen, dus het is waarschijnlijk dat we dingen in een andere volgorde zullen bevrijden dan waarin we ze hebben gemaakt.
De heap is dus veel complexer, omdat er uiteindelijk geheugengebieden zijn die ongebruikt zijn, doorschoten met brokken die - het geheugen wordt gefragmenteerd. Het vinden van vrij geheugen van de grootte die u nodig hebt, is een moeilijk probleem. Dit is de reden waarom de hoop moet worden vermeden (hoewel deze nog steeds vaak wordt gebruikt).
Implementatie
Implementatie van zowel de stack als de heap is meestal afhankelijk van de runtime / OS. Vaak creëren games en andere toepassingen die van cruciaal belang zijn voor de prestaties hun eigen geheugenoplossingen die een groot deel van het geheugen uit de hoop halen en het vervolgens intern uitdelen om te voorkomen dat ze voor geheugen afhankelijk zijn van het besturingssysteem.
Dit is alleen praktisch als je geheugengebruik behoorlijk verschilt van de norm - dat wil zeggen voor games waarbij je een level laadt in één grote operatie en de hele boel weg kunt gooien in een andere enorme operatie.
Fysieke locatie in geheugen
Dit is minder relevant dan je denkt vanwege een technologie genaamd Virtual Memory die je programma doet denken dat je toegang hebt tot een bepaald adres waar de fysieke data ergens anders staat (zelfs op de harde schijf!). De adressen die u voor de stapel krijgt, worden in oplopende volgorde weergegeven naarmate uw oproepboom dieper wordt. De adressen voor de heap zijn onvoorspelbaar (d.w.z. implementatie-specifiek) en eerlijk gezegd niet belangrijk.
|
Ter verduidelijking: dit antwoord bevat onjuiste informatie (thomas heeft zijn antwoord opgelost na opmerkingen, cool :)). Andere antwoorden vermijden gewoon uit te leggen wat statische toewijzing betekent. Dus ik zal de drie belangrijkste vormen van toewijzing uitleggen en hoe ze zich gewoonlijk verhouden tot het heap-, stack- en datasegment hieronder. Ik zal ook enkele voorbeelden laten zien in zowel C / C ++ als Python om mensen te helpen begrijpen.
"Statische" (AKA statisch toegewezen) variabelen worden niet op de stapel toegewezen. Ga er niet van uit - veel mensen doen dat alleen omdat "statisch" veel op "stapel" lijkt. Ze bestaan ​​eigenlijk niet in de stapel, noch in de hoop. Ze maken deel uit van wat het gegevenssegment wordt genoemd.
Het is echter over het algemeen beter om rekening te houden met "scope" en "levensduur" in plaats van "stack" en "heap".
Bereik verwijst naar welke delen van de code toegang hebben tot een variabele. Over het algemeen denken we aan lokale scope (alleen toegankelijk via de huidige functie) versus globale scope (overal toegankelijk), hoewel de scope veel complexer kan worden.
Levensduur verwijst naar wanneer een variabele wordt toegewezen en ongedaan gemaakt tijdens de uitvoering van het programma. Meestal denken we aan statische allocatie (variableblijft bestaan ​​gedurende de gehele duur van het programma, waardoor het handig is om dezelfde informatie op te slaan over verschillende functieaanroepen) versus automatische toewijzing (variabele blijft alleen bestaan ​​tijdens een enkele aanroep van een functie, waardoor het handig is voor het opslaan van informatie die alleen wordt gebruikt tijdens uw functie en kan worden weggegooid als u klaar bent) versus dynamische toewijzing (variabelen waarvan de duur wordt gedefinieerd tijdens runtime, in plaats van compilatietijd zoals statisch of automatisch).
Hoewel de meeste compilers en interpreters dit gedrag op dezelfde manier implementeren in termen van het gebruik van stapels, hopen, enz., Kan een compiler deze conventies soms breken als hij wil, zolang het gedrag correct is. Door optimalisatie kan het bijvoorbeeld zijn dat een lokale variabele alleen in een register voorkomt of volledig wordt verwijderd, ook al bestaan ​​de meeste lokale variabelen in de stapel. Zoals in een paar opmerkingen is opgemerkt, ben je vrij om een ​​compiler te implementeren die niet eens een stapel of een hoop gebruikt, maar in plaats daarvan een aantal andere opslagmechanismen (zelden gedaan, aangezien stapels en hopen hier geweldig voor zijn).
Ik zal enkele eenvoudige geannoteerde C-code geven om dit alles te illustreren. De beste manier om te leren is door een programma onder een debugger uit te voeren en het gedrag te bekijken. Als je python liever leest, ga dan naar het einde van het antwoord :)
// Statisch toegewezen in het gegevenssegment wanneer het programma / de DLL voor het eerst wordt geladen
// Opgehaald wanneer het programma / DLL wordt afgesloten
// scope - is overal in de code toegankelijk
int someGlobalVariable;
// Statisch toegewezen in het gegevenssegment wanneer het programma voor het eerst wordt geladen
// Opgehaald wanneer het programma / DLL wordt afgesloten
// scope - is overal in dit specifieke codebestand toegankelijk
statische int someStaticVariable;
// "someArgument" wordt op de stapel toegewezen telkens als MyFunction wordt aangeroepen
// "someArgument" wordt ongedaan gemaakt wanneer MyFunction terugkeert
// scope - is alleen toegankelijk binnen MyFunction ()
void MyFunction (int someArgument) {
// Statisch toegewezen in het gegevenssegment wanneer het programma voor het eerst wordt geladen
// Opgehaald wanneer het programma / DLL wordt afgesloten
// scope - is alleen toegankelijk binnen MyFunction ()
statische int someLocalStaticVariable;
// Toegewezen op de stapel elke keer dat MyFunction wordt aangeroepen
// Opgehaald wanneer MyFunction terugkeert
// scope - is alleen toegankelijk binnen MyFunction ()
int someLocalVariable;
// Een * pointer * wordt op de stapel toegewezen telkens wanneer MyFunction wordt aangeroepen
// Deze aanwijzer wordt ongedaan gemaakt wanneer MyFunction terugkeert
// scope - de aanwijzer is alleen toegankelijk binnen MyFunction ()
int * someDynamicVariable;
// Deze regel zorgt ervoor dat ruimte voor een geheel getal in de heap wordt toegewezen
// wanneer deze regel wordt uitgevoerd. Merk op dat dit niet aan het begin is van
// de aanroep van MyFunction (), zoals de automatische variabelen
// scope - alleen code binnen MyFunction () heeft toegang tot deze ruimte
// * via deze specifieke variabele *.
// Als u het adres echter ergens anders doorgeeft, wordt die code
// heeft er ook toegang toe
someDynamicVariable = nieuwe int;
// Deze regel deelt de ruimte voor het gehele getal in de heap uit.
// Als we het niet zouden schrijven, zou het geheugen "gelekt" zijn.
// Let op een fundamenteel verschil tussen de stapel en de heap
// de heap moet worden beheerd. De stack wordt voor ons beheerd.
verwijder someDynamicVariable;
// In andere gevallen, in plaats van deze heapruimte ongedaan te maken, u
// kan het adres ergens meer permanent opslaan om later te gebruiken.
// Sommige talen zorgen zelfs voor deallocation voor u ... maar
// altijd moet er tijdens runtime door een of ander mechanisme voor worden gezorgd.
// Wanneer de functie retourneert, someArgument, someLocalVariable
// en de aanwijzer someDynamicVariable zijn ongedaan gemaakt.
// De spatie waarnaar wordt verwezen door someDynamicVariable was al
// vrijgelaten alvorens terug te keren.
terugkeren;
}
// Merk op dat someGlobalVariable, someStaticVariable en
// someLocalStaticVariable blijft bestaan, en zijn dat niet
// toegewezen totdat het programma wordt afgesloten.
Een bijzonder schrijnend voorbeeld van waarom het belangrijk is om onderscheid te maken tussen levensduur en bereik, is dat een variabele een lokaal bereik kan hebben, maar een statische levensduur - bijvoorbeeld "someLocalStaticVariable" in het codevoorbeeld hierboven. Dergelijke variabelen kunnen onze gebruikelijke maar informele naamgevingsgewoonten erg verwarrend maken. Als we bijvoorbeeld 'lokaal' zeggen, bedoelen we meestal 'automatisch toegewezen variabele met lokaal bereik' en als we globaal zeggen, bedoelen we meestal 'statisch toegewezen variabele met een globaal bereik'. Helaas als het gaat om zaken als "statisch toegewezen variabelen met een bestandsbereik" zeggen veel mensen gewoon ... "huh ???".
Sommige syntaxiskeuzes in C / C ++ verergeren dit probleem - veel mensen denken bijvoorbeeld dat globale variabelen niet "statisch" zijn vanwege de syntaxis die hieronder wordt weergegeven.
int var1; // Heeft een globaal bereik en statische toewijzing
statische int var2; // Heeft bestandsbereik en statische toewijzing
int main () {return 0;}
Merk op dat het plaatsen van het sleutelwoord "statisch" in de bovenstaande declaratie verhindert dat var2 een globaal bereik heeft. Desalniettemin heeft de globale var1 een statische toewijzing. Dit is nietintuïtief! Om deze reden probeer ik nooit het woord "statisch" te gebruiken bij het beschrijven van bereik, en in plaats daarvan iets te zeggen als "bestand" of "bestand beperkt" bereik. Veel mensen gebruiken echter de uitdrukking "statisch" of "statisch bereik" om een ​​variabele te beschrijven die alleen toegankelijk is vanuit één codebestand. In de context van levensduur betekent "statisch" altijd dat de variabele wordt toegewezen bij het starten van het programma en wordt opgeheven wanneer het programma wordt afgesloten.
Sommige mensen beschouwen deze concepten als specifiek voor C / C ++. Zij zijn niet. Het Python-voorbeeld hieronder illustreert bijvoorbeeld alle drie de soorten toewijzing (er zijn enkele subtiele verschillen mogelijk in geïnterpreteerde talen waar ik hier niet op inga).
van datetime import datetime
klasse Dier:
_FavoriteFood = 'Undefined' # _FavoriteFood is statisch toegewezen
def PetAnimal (zelf):
curTime = datetime.time (datetime.now ()) # curTime wordt automatisch toegewezen
print ("Bedankt dat je me aait. Maar het is" + str (curTime) + ", je moet me eten geven. Mijn favoriete eten is" + self._FavoriteFood)
klasse Cat (Animal):
_FavoriteFood = 'tonijn' # Opmerking, aangezien we overschrijven, heeft de Cat-klasse zijn eigen statisch toegewezen _FavoriteFood-variabele, anders dan die van Animal
klasse Hond (Dier):
_FavoriteFood = 'steak' # Evenzo krijgt de klasse Dog zijn eigen statische variabele. Belangrijk om op te merken - deze ene statische variabele wordt gedeeld door alle instanties van Dog, en is daarom niet dynamisch!
if __name__ == "__main__":
snorharen = Cat () # Dynamisch toegewezen
fido = Dog () # Dynamisch toegewezen
rinTinTin = Dog () # Dynamisch toegewezen
snorharen.PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
Dog._FavoriteFood = 'melkbeenderen'
snorharen.PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
# Uitvoer is:
# Bedankt dat je me aait. Maar het is 13: 05: 02.255000, je moet me eten geven. Mijn favoriete eten is tonijn
# Bedankt dat je me aait. Maar het is 13: 05: 02.255000, je moet me eten geven. Mijn favoriete eten is biefstuk
# Bedankt dat je me aait. Maar het is 13: 05: 02.255000, je moet me eten geven. Mijn favoriete eten is biefstuk
# Bedankt dat je me aait. Maar het is 13: 05: 02.255000, je moet me eten geven. Mijn favoriete eten is tonijn
# Bedankt dat je me aait. Maar het is 13: 05: 02.255000, je moet me eten geven. Mijn favoriete eten is melkbeenderen
# Bedankt dat je me aait. Maar het is 13: 05: 02.256000, je moet me eten geven. Mijn favoriete eten is melkbeenderen
|
Anderen hebben de grote lijnen redelijk goed beantwoord, dus ik zal een paar details geven.
Stapel en hoop hoeven niet enkelvoud te zijn. Een veel voorkomende situatie waarin u meer dan één stapel heeft, is als u meer dan één thread in een proces heeft. In dit geval heeft elke thread zijn eigen stapel. U kunt ook meer dan één heap hebben. Sommige DLL-configuraties kunnen er bijvoorbeeld toe leiden dat verschillende DLL's worden toegewezen vanaf verschillende Heap, daarom is het over het algemeen een slecht idee om geheugen vrij te maken dat is toegewezen door een andere bibliotheek.
In C kun je het voordeel krijgen van variabele lengtetoewijzing door het gebruik van alloca, dat op de stapel toewijst, in tegenstelling tot alloc, dat op de heap toewijst. Dit geheugen zal uw retourverklaring niet overleven, maar het is handig voor een kladbuffer.
Het maken van een enorme tijdelijke buffer op Windows waar u niet veel van gebruikt, is niet gratis. Dit komt omdat de compiler een stack-probe-lus zal genereren die elke keer dat uw functie wordt ingevoerd, wordt aangeroepen om ervoor te zorgen dat de stack bestaat (omdat Windows een enkele bewakingspagina aan het einde van uw stack gebruikt om te detecteren wanneer de stack moet worden uitgebreid. Als u meer dan één pagina buiten het einde van de stapel geheugen opent, loopt u vast). Voorbeeld:
ongeldig mijnfunctie ()
{
char big [10000000];
// Doe iets dat 99% van de tijd alleen gebruikt voor de eerste 1K van big.
}
|
Anderen hebben uw vraag direct beantwoord, maar als ik de stapel en de hoop probeer te begrijpen, denk ik dat het nuttig is om de geheugenlay-out van een traditioneel UNIX-proces (zonder threads en op mmap () gebaseerde allocators) te overwegen. De webpagina Geheugenbeheer Woordenlijst heeft een diagram van deze geheugenlay-out.
De stack en heap bevinden zich traditioneel aan tegenovergestelde uiteinden van de virtuele adresruimte van het proces. De stapel groeit automatisch wanneer deze wordt benaderd, tot een grootte die is ingesteld door de kernel (die kan worden aangepast met setrlimit (RLIMIT_STACK, ...)). De heap wordt groter wanneer de geheugenallocator de systeemaanroep brk () of sbrk () aanroept, waardoor meer pagina's fysiek geheugen worden toegewezen aan de virtuele adresruimte van het proces.
In systemen zonder virtueel geheugen, zoals sommige embedded systemen, is vaak dezelfde basislayout van toepassing, behalve dat de stack en heap een vaste grootte hebben. In andere ingebedde systemen (zoals die gebaseerd op Microchip PIC-microcontrollers) is de programmastapel echter een afzonderlijk blok geheugen dat niet kan worden geadresseerd door instructies voor gegevensverplaatsing, en kan alleen worden gewijzigd of indirect worden gelezen via instructies voor programmastroom terugkeer, enz.). Andere architecturen, zoals Intel Itanium-processors, hebben meerdere stapels. In die zin is de stack een onderdeel van de CPU-architectuur.
|
De stapel is een portiegeheugen dat kan worden gemanipuleerd via verschillende belangrijke assembleertaalinstructies, zoals 'pop' (verwijder en retourneer een waarde uit de stapel) en 'push' (push een waarde naar de stapel), maar roep ook (roep een subroutine aan - dit drukt op het adres om terug te keren naar de stapel) en terugkeer (terugkeer van een subroutine - hierdoor springt het adres van de stapel en springt ernaartoe). Het is het geheugengebied onder het stapelaanwijzerregister, dat naar wens kan worden ingesteld. De stapel wordt ook gebruikt om argumenten door te geven aan subroutines, en ook om de waarden in registers te behouden voordat subroutines worden aangeroepen.
De heap is een gedeelte van het geheugen dat door het besturingssysteem aan een toepassing wordt gegeven, meestal via een syscall-achtige malloc. Op moderne besturingssystemen is dit geheugen een set pagina's waartoe alleen het aanroepproces toegang heeft.
De grootte van de stapel wordt bepaald tijdens runtime en neemt over het algemeen niet toe nadat het programma is gestart. In een C-programma moet de stapel groot genoeg zijn om elke variabele te bevatten die binnen elke functie wordt gedeclareerd. De heap zal dynamisch groeien als dat nodig is, maar het besturingssysteem doet uiteindelijk de aanroep (het zal de heap vaak met meer groeien dan de waarde die door malloc wordt gevraagd, zodat in ieder geval sommige toekomstige mallocs niet terug hoeven naar de kernel om meer geheugen krijgen. Dit gedrag kan vaak worden aangepast)
Omdat je de stack hebt toegewezen voordat je het programma start, hoef je nooit malloc te maken voordat je de stack kunt gebruiken, dus dat is daar een klein voordeel. In de praktijk is het erg moeilijk te voorspellen wat snel en wat traag zal zijn in moderne besturingssystemen met virtuele geheugensubsystemen, want hoe de pagina's worden geïmplementeerd en waar ze worden opgeslagen, is een implementatiedetail.
|
Wat is een stapel?
Een stapel is een stapel objecten, meestal een die netjes is gerangschikt.
Stapels in computerarchitecturen zijn geheugengebieden waar gegevens op een last-in-first-out manier worden toegevoegd of verwijderd.
In een multi-threaded applicatie heeft elke thread zijn eigen stapel.
Wat is een hoopje?
Een hoop is een slordige verzameling dingen die lukraak opgestapeld zijn.
In computerarchitecturen is de heap een gebied met dynamisch toegewezen geheugen dat automatisch wordt beheerd door het besturingssysteem of de geheugenbeheerbibliotheek.
Geheugen op de heap wordt tijdens de uitvoering van het programma regelmatig toegewezen, ongedaan gemaakt en van grootte veranderd, en dit kan leiden tot een probleem dat fragmentatie wordt genoemd.
Fragmentatie treedt op wanneer geheugenobjecten worden toegewezen met kleine tussenruimten die te klein zijn om extra geheugenobjecten te bevatten.
Het nettoresultaat is een percentage van de heapruimte dat niet bruikbaar is voor verdere geheugentoewijzingen.
Beide samen
In een multi-threaded applicatie heeft elke thread zijn eigen stapel. Maar alle verschillende threads zullen de hoop delen.
Omdat de verschillende threads de heap delen in een multi-threaded applicatie, betekent dit ook dat er enige coördinatie tussen de threads moet zijn, zodat ze niet proberen toegang te krijgen tot hetzelfde stuk (s) geheugen in de heap op en deze te manipuleren. dezelfde tijd.
Wat is sneller - de stapel of de hoop? En waarom?
De stapel is veel sneller dan de hoop.
Dit komt door de manier waarop geheugen op de stapel wordt toegewezen.
Het toewijzen van geheugen op de stapel is net zo eenvoudig als het verplaatsen van de stapelwijzer omhoog.
Voor mensen die nog geen ervaring hebben met programmeren, is het waarschijnlijk een goed idee om de stapel te gebruiken, omdat dit gemakkelijker is.
Omdat de stapel klein is, zou u deze willen gebruiken als u precies weet hoeveel geheugen u nodig heeft voor uw gegevens, of als u weet dat de omvang van uw gegevens erg klein is.
Het is beter om de heap te gebruiken als u weet dat u veel geheugen nodig heeft voor uw gegevens, of als u gewoon niet zeker weet hoeveel geheugen u nodig heeft (zoals bij een dynamische array).
Java-geheugenmodel
De stapel is het geheugengebied waar lokale variabelen (inclusief methodeparameters) worden opgeslagen. Als het om objectvariabelen gaat, zijn dit slechts verwijzingen (verwijzingen) naar de feitelijke objecten op de heap.
Elke keer dat een object wordt geïnstantieerd, wordt een stuk heap-geheugen gereserveerd om de gegevens (status) van dat object vast te houden. Omdat objecten andere objecten kunnen bevatten, kunnen sommige van deze gegevens in feite verwijzingen naar die geneste objecten bevatten.
|
Ik denk dat veel andere mensen u op dit punt grotendeels de juiste antwoorden hebben gegeven.
Een detail dat echter is gemist, is dat de "heap" in feite waarschijnlijk de "gratis winkel" moet worden genoemd. De reden voor dit onderscheid is dat de oorspronkelijke gratis winkel werd geïmplementeerd met een gegevensstructuur die bekend staat als een "binominale heap". Om die reden was toewijzing vanaf vroege implementaties van malloc () / free () toewijzing vanaf een hoop. Tegenwoordig zijn de meeste gratis winkels echter geïmplementeerd met zeer uitgebreide datastructuren die geen binominale hopen zijn.
|
Je kunt een aantal interessante dingen doen met de stapel. U hebt bijvoorbeeld functies zoals alloca (ervan uitgaande dat u voorbij de uitgebreide waarschuwingen over het gebruik ervan kunt komen), wat een vorm van malloc is diegebruikt specifiek de stapel, niet de heap, voor geheugen.
Dat gezegd hebbende, zijn stack-gebaseerde geheugenfouten enkele van de ergste die ik heb meegemaakt. Als u heap-geheugen gebruikt en u overschrijdt de grenzen van uw toegewezen blok, heeft u een behoorlijke kans om een ​​segmentfout te veroorzaken. (Niet 100%: uw blok kan incidenteel aangrenzend zijn aan een ander blok dat u eerder hebt toegewezen.) Maar aangezien variabelen die op de stapel zijn gemaakt altijd aan elkaar grenzen, kan het wegschrijven de waarde van een andere variabele wijzigen. Ik heb geleerd dat wanneer ik het gevoel heb dat mijn programma niet meer aan de wetten van de logica voldoet, het waarschijnlijk een bufferoverloop is.
|
Simpel gezegd, de stapel is waar lokale variabelen worden gemaakt. Elke keer dat u een subroutine aanroept, wordt ook de programmateller (pointer naar de volgende machine-instructie) en alle belangrijke registers en soms worden de parameters op de stapel geduwd. Vervolgens worden alle lokale variabelen binnen de subroutine op de stapel geduwd (en van daaruit gebruikt). Wanneer de subroutine is afgelopen, wordt dat spul allemaal weer van de stapel geschopt. De pc- en registergegevens worden opgehaald en teruggezet waar het was zoals het was, zodat uw programma zijn vrolijke gang kan gaan.
De heap is het gebied van geheugen waarop dynamische geheugentoewijzingen worden gemaakt (expliciete "nieuwe" of "allocate" oproepen). Het is een speciale gegevensstructuur die geheugenblokken van verschillende grootten en hun toewijzingsstatus kan bijhouden.
In "klassieke" systemen was RAM zo ingedeeld dat de stapelaanwijzer onderaan het geheugen begon, de heapaanwijzer bovenaan begon en ze naar elkaar toe groeiden. Als ze elkaar overlappen, heb je geen RAM meer. Dat werkt echter niet met moderne multi-threaded besturingssystemen. Elke thread moet zijn eigen stapel hebben en die kunnen dynamisch worden gemaakt.
|
Van WikiAnwser.
Stapel
Wanneer een functie of methode een andere functie aanroept die op zijn beurt een andere functie aanroept, enz., Blijft de uitvoering van al die functies onderbroken totdat de allerlaatste functie zijn waarde teruggeeft.
Deze reeks onderbroken functieaanroepen is de stapel, omdat elementen in de stapel (functieaanroepen) van elkaar afhankelijk zijn.
De stapel is belangrijk om te overwegen bij het afhandelen van uitzonderingen en het uitvoeren van threads.
Hoop
De heap is gewoon het geheugen dat door programma's wordt gebruikt om variabelen op te slaan.
Element van de heap (variabelen) hebben geen afhankelijkheden met elkaar en kunnen altijd willekeurig en op elk moment worden benaderd.
|
Stapel
Zeer snelle toegang
U hoeft variabelen niet expliciet vrij te geven
De ruimte wordt efficiënt beheerd door de CPU, het geheugen raakt niet gefragmenteerd
Alleen lokale variabelen
Beperking van stapelgrootte (afhankelijk van besturingssysteem)
Variabelen kunnen niet worden vergroot of verkleind
Hoop
Variabelen zijn wereldwijd toegankelijk
Geen limiet op geheugengrootte
(Relatief) langzamere toegang
Geen gegarandeerd efficiënt gebruik van de ruimte, geheugen kan in de loop van de tijd gefragmenteerd raken als geheugenblokken worden toegewezen en vervolgens vrijgemaakt
U moet het geheugen beheren (u bent verantwoordelijk voor het toewijzen en vrijmaken van variabelen)
Variabelen kunnen worden aangepast met realloc ()
|
In het kort
Een stapel wordt gebruikt voor statische geheugentoewijzing en een heap voor dynamische geheugentoewijzing, beide opgeslagen in het RAM van de computer.
In detail
De stapel
De stack is een "LIFO" (last in, first out) datastructuur, die vrij nauw wordt beheerd en geoptimaliseerd door de CPU. Elke keer dat een functie een nieuwe variabele declareert, wordt deze op de stapel "geduwd". Elke keer dat een functie wordt afgesloten, worden alle variabelen die door die functie op de stapel worden geduwd, vrijgegeven (dat wil zeggen, ze worden verwijderd). Zodra een stapelvariabele is vrijgegeven, komt dat geheugengebied beschikbaar voor andere stapelvariabelen.
Het voordeel van het gebruik van de stack om variabelen op te slaan, is dat het geheugen voor u wordt beheerd. U hoeft geen geheugen handmatig toe te wijzen, of het vrij te maken als u het niet meer nodig heeft. Bovendien, omdat de CPU het stapelgeheugen zo efficiënt organiseert, gaat het lezen van en schrijven naar stapelvariabelen erg snel.
Meer vind je hier.
De hoop
De heap is een gebied van het geheugen van uw computer dat niet automatisch voor u wordt beheerd en niet zo strak wordt beheerd door de CPU. Het is een meer vrij zwevend geheugengebied (en is groter). Om geheugen op de heap toe te wijzen, moet u malloc () of calloc () gebruiken, dit zijn ingebouwde C-functies. Als je eenmaal geheugen op de heap hebt toegewezen, ben je verantwoordelijk voor het gebruik van free () om dat geheugen ongedaan te maken zodra je het niet meer nodig hebt.
Als u dit niet doet, heeft uw programma een zogenaamd geheugenlek. Dat wil zeggen dat geheugen op de heap nog steeds opzij wordt gezet (en niet beschikbaar is voor andere processen). Zoals we zullen zien in het gedeelte over foutopsporing, is er een tool genaamd Valgrind die u kan helpen bij het opsporen van geheugenlekken.
In tegenstelling tot de stapel heeft de heap geen groottebeperkingen voor variabele grootte (afgezien van de voor de hand liggende fysieke beperkingen van uw computer). Heap-geheugen is iets langzamer om van en naar geschreven te worden, omdat men pointers moet gebruiken om toegang te krijgen tot het geheugen op de heap. We zullen het binnenkort over aanwijzingen hebben.
In tegenstelling tot de stapel,variabelen die op de heap zijn gemaakt, zijn toegankelijk voor elke functie, overal in uw programma. Heap-variabelen hebben in wezen een globaal bereik.
Meer vind je hier.
Variabelen die op de stapel zijn toegewezen, worden rechtstreeks in het geheugen opgeslagen en de toegang tot dit geheugen is erg snel, en de toewijzing ervan wordt afgehandeld wanneer het programma wordt gecompileerd. Wanneer een functie of methode een andere functie aanroept die op zijn beurt een andere functie aanroept, enz., Blijft de uitvoering van al die functies onderbroken totdat de allerlaatste functie zijn waarde teruggeeft. De stapel is altijd gereserveerd in een LIFO-volgorde, het meest recent gereserveerde blok is altijd het volgende blok dat moet worden vrijgemaakt. Dit maakt het heel eenvoudig om de stapel bij te houden, een blok vrijmaken van de stapel is niets meer dan het aanpassen van één aanwijzer.
Van variabelen die op de heap zijn toegewezen, wordt hun geheugen toegewezen tijdens runtime en toegang tot dit geheugen is iets langzamer, maar de grootte van de heap wordt alleen beperkt door de grootte van het virtuele geheugen. Elementen van de heap zijn niet afhankelijk van elkaar en zijn altijd willekeurig en op elk moment toegankelijk. U kunt op elk moment een blok toewijzen en op elk moment vrijmaken. Dit maakt het veel complexer om op een bepaald moment bij te houden welke delen van de heap zijn toegewezen of vrij zijn.
U kunt de stapel gebruiken als u precies weet hoeveel gegevens u moet toewijzen voordat u compileert, en als deze niet te groot is. U kunt de heap gebruiken als u niet precies weet hoeveel gegevens u tijdens runtime nodig heeft of als u veel gegevens moet toewijzen.
In een situatie met meerdere threads heeft elke thread zijn eigen volledig onafhankelijke stapel, maar ze zullen de heap delen. De stapel is thread-specifiek en de heap is toepassingsspecifiek. De stapel is belangrijk om te overwegen bij het afhandelen van uitzonderingen en het uitvoeren van threads.
Elke thread krijgt een stapel, terwijl er meestal maar één heap is voor de applicatie (hoewel het niet ongebruikelijk is om meerdere hopen te hebben voor verschillende soorten toewijzingen).
Als de applicatie tijdens runtime meer heap nodig heeft, kan deze geheugen uit vrij geheugen toewijzen en als de stack geheugen nodig heeft, kan het geheugen toewijzen uit het vrij toegewezen geheugen voor de applicatie.
Hier en hier worden zelfs meer details gegeven.
Kom nu naar de antwoorden van uw vraag.
In hoeverre worden ze gecontroleerd door het besturingssysteem of de taalruntime?
Het besturingssysteem wijst de stapel toe voor elke thread op systeemniveau wanneer de thread wordt gemaakt. Meestal wordt het besturingssysteem aangeroepen door de taalruntime om de heap voor de toepassing toe te wijzen.
Meer vind je hier.
Wat is hun reikwijdte?
Al bovenaan vermeld.
"U kunt de stapel gebruiken als u precies weet hoeveel gegevens u moet toewijzen voordat u compileert, en deze is niet te groot. U kunt de stapel gebruiken als u niet precies weet hoeveel gegevens u tijdens runtime nodig heeft of als je moet veel gegevens toewijzen. "
Meer is hier te vinden.
Wat bepaalt de grootte van elk van hen?
De grootte van de stapel wordt bepaald door OS wanneer een thread wordt gemaakt. De grootte van de heap wordt ingesteld bij het opstarten van de toepassing, maar deze kan toenemen naarmate er ruimte nodig is (de allocator vraagt ​​meer geheugen van het besturingssysteem).
Wat maakt iemand sneller?
Stacktoewijzing is veel sneller omdat het alleen maar de stapelaanwijzer verplaatst. Door geheugenpools te gebruiken, kunt u vergelijkbare prestaties halen uit heap-toewijzing, maar dat gaat gepaard met een beetje extra complexiteit en zijn eigen hoofdpijn.
Stack vs. heap is ook niet alleen een prestatie-overweging; het vertelt je ook veel over de verwachte levensduur van objecten.
Details zijn hier te vinden.
|
OK, eenvoudig en in korte woorden, ze bedoelen besteld en niet besteld ...!
Stapelen: In stapelitems komen dingen op elkaar te liggen, wat betekent dat ze sneller en efficiënter kunnen worden verwerkt! ...
Er is dus altijd een index om het specifieke item aan te wijzen, ook de verwerking gaat sneller, er is ook een relatie tussen de items! ...
Heap: geen volgorde, de verwerking zal langzamer zijn en waarden worden samen verknoeid zonder specifieke volgorde of index ... er zijn willekeurige en er is geen relatie tussen hen ... dus de uitvoering en gebruikstijd kunnen variëren ...
Ik maak ook de onderstaande afbeelding om te laten zien hoe ze eruit kunnen zien:
|
stack, heap en gegevens van elk proces in virtueel geheugen:
|
In de jaren tachtig verspreidde UNIX zich als konijntjes met grote bedrijven die hun eigen rol speelden.
Exxon had er een, net als tientallen merknamen die in de geschiedenis verloren zijn gegaan.
Hoe de herinnering was aangelegd, was ter beoordeling van de vele uitvoerders.
Een typisch C-programma werd plat neergelegd in het geheugen met
een kans om te vergroten door de waarde brk () te wijzigen.
Meestal was de HEAP net onder deze brk-waarde
en toenemende brk verhoogde de hoeveelheid beschikbare heap.
De enkele STACK was typisch een gebied onder HEAP dat een stukje geheugen was
met niets van waarde tot de top van het volgende vaste geheugenblok.
Dit volgende blok was vaak CODE dat overschreven kon worden door stackdata
in een van de beroemde hacks van zijn tijd.
Een typisch geheugenblok was BSS (een blok van nulwaarden)
die per ongeluk niet op nul werd gezet in het aanbod van een fabrikant.
Een andere was DATA met geïnitialiseerde waarden, inclusief strings en getallen.
Een derde was CODE met CRT (C runtime), hoofdfuncties, functies en bibliotheken.
De komst van virtueel geheugen in UNIX verandert veel van de beperkingen.
Er is geen objectieve reden waarom deze blokken aaneengesloten moeten zijn,
of vast in maat, of nu op een bepaalde manier besteld.
Voordat UNIX natuurlijk Multics was, die niet onder deze beperkingen leed.
Hier is een schema dat een van de geheugenlay-outs van die tijd laat zien.
|
Een paar cent: ik denk dat het goed zal zijn om het geheugen grafisch en eenvoudiger te tekenen:
Pijlen - laten zien waar de groeistapel en heap, de grootte van de processtapel een limiet hebben, gedefinieerd in het besturingssysteem, de limieten voor de grootte van de threadstapel door parameters in de API voor het maken van threads. Heap beperkt meestal de maximale grootte van het virtuele geheugen door het proces, bijvoorbeeld voor 32 bit 2-4 GB.
Zo eenvoudige manier: procesheap is algemeen voor het proces en alle threads erin, gebruikt voor geheugentoewijzing in het algemeen met zoiets als malloc ().
Stack is een snel geheugen voor het opslaan van functieretourpointers en variabelen in algemene gevallen, verwerkt als parameters in functieaanroep, lokale functievariabelen.
|
Omdat sommige antwoorden muggenziften waren, ga ik mijn steentje bijdragen.
Verrassend genoeg heeft niemand gezegd dat meerdere (dwz niet gerelateerd aan het aantal actieve threads op OS-niveau) call-stacks niet alleen in exotische talen (PostScript) of platforms (Intel Itanium) te vinden zijn, maar ook in vezels, groene threads en enkele implementaties van coroutines.
Vezels, groene draden en coroutines zijn in veel opzichten vergelijkbaar, wat tot veel verwarring leidt. Het verschil tussen vezels en groene draden is dat de eerste coöperatieve multitasking gebruiken, terwijl de laatste coöperatieve of preventieve (of zelfs beide) kan hebben. Zie hier voor het onderscheid tussen vezels en coroutines.
In elk geval is het doel van beide vezels, groene draden en coroutines om meerdere functies tegelijkertijd uit te voeren, maar niet parallel (zie deze SO-vraag voor het onderscheid) binnen een enkele OS-thread, waarbij de controle van elkaar wordt overgebracht. op een georganiseerde manier.
Bij gebruik van vezels, groene draden of coroutines heb je meestal een aparte stapel per functie. (Technisch gezien is niet alleen een stapel maar een hele uitvoeringscontext per functie. Het belangrijkste is dat CPU-registers.) Voor elke thread zijn er evenveel stapels als er gelijktijdig actieve functies zijn, en de thread schakelt tussen het uitvoeren van elke functie volgens de logica van uw programma. Wanneer een functie tot het einde loopt, wordt de stapel vernietigd. Het aantal en de levensduur van stapels zijn dus dynamisch en worden niet bepaald door het aantal threads op OS-niveau!
Merk op dat ik zei "meestal een aparte stapel per functie". Er zijn zowel stapelbare als stapelloze implementaties van couroutines. De meest opvallende stackful C ++ implementaties zijn Boost.Coroutine en Microsoft PPL's ​​async / await. (De hervatbare functies van C ++ (ook bekend als "async en await"), die werden voorgesteld voor C ++ 17, gebruiken waarschijnlijk stackless coroutines.)
Het voorstel van Fibers voor de C ++ -standaardbibliotheek komt eraan. Er zijn ook enkele bibliotheken van derden. Groene draden zijn enorm populair in talen als Python en Ruby.
|
Ik heb iets te delen, hoewel de belangrijkste punten al zijn behandeld.
Stapel
Zeer snelle toegang.
Opgeslagen in RAM.
Functie-aanroepen worden hier samen met de doorgegeven lokale variabelen en functieparameters geladen.
Er wordt automatisch ruimte vrijgemaakt wanneer het programma buiten een bereik valt.
Opgeslagen in sequentieel geheugen.
Hoop
Trage toegang in vergelijking met Stack.
Opgeslagen in RAM.
Dynamisch gecreëerde variabelen worden hier opgeslagen, wat later het toegewezen geheugen na gebruik vrijmaakt.
Opgeslagen overal waar geheugentoewijzing plaatsvindt, altijd toegankelijk via aanwijzer.
Interessante opmerking:
Als de functieaanroepen in heap waren opgeslagen, had dit geresulteerd in 2 rommelige punten:
Door opeenvolgende opslag in stapel is de uitvoering sneller. Opslag in heap zou hebben geleid tot een enorm tijdverbruik, waardoor het hele programma langzamer zou worden uitgevoerd.
Als functies in heap waren opgeslagen (rommelige opslag met aanwijzer), zou er geen manier zijn geweest om terug te keren naar het adres van de beller (die stapel geeft vanwege opeenvolgende opslag in het geheugen).
|
Wauw! Zoveel antwoorden en ik denk niet dat een van hen het goed heeft gedaan ...
1) Waar en wat zijn ze (fysiek in het geheugen van een echte computer)?
De stapel is het geheugen dat begint als het hoogste geheugenadres dat aan uw programma-afbeelding is toegewezen en vanaf daar in waarde afneemt. Het is gereserveerd voor aangeroepen functieparameters en voor alle tijdelijke variabelen die in functies worden gebruikt.
Er zijn twee hopen: openbaar en privé.
De privéheap begint op een grens van 16 bytes (voor 64-bits programma's) of een grens van 8 bytes (voor 32-bits programma's) na de laatste byte aan code in uw programma, en neemt vervolgens toe metwaarde vanaf daar. Het wordt ook wel de standaardheap genoemd.
Als de privéheap te groot wordt, zal deze het stapelgebied overlappen, net zoals de stapel de heap zal overlappen als deze te groot wordt. Omdat de stapel begint op een hoger adres en zich een weg baant naar een lager adres, kunt u met de juiste hacking de stapel zo groot maken dat deze het privéheapgebied overschrijdt en het codegebied overlapt. De truc is dan om genoeg van het codegebied te overlappen dat u in de code kunt haken. Het is een beetje lastig om te doen en u riskeert een programma-crash, maar het is gemakkelijk en zeer effectief.
De openbare heap bevindt zich in zijn eigen geheugenruimte buiten de afbeeldingsruimte van uw programma. Het is dit geheugen dat wordt overgeheveld naar de harde schijf als de geheugenbronnen schaars worden.
2) In hoeverre worden ze gecontroleerd door het besturingssysteem of de taalruntime?
De stack wordt beheerd door de programmeur, de private heap wordt beheerd door het besturingssysteem en de publieke heap wordt door niemand beheerd omdat het een OS-service is - je doet verzoeken en deze worden toegekend of geweigerd.
2b) Wat is hun reikwijdte?
Ze zijn allemaal globaal voor het programma, maar hun inhoud kan privé, openbaar of globaal zijn.
2c) Wat bepaalt de grootte van elk van hen?
De grootte van de stapel en de privéheap worden bepaald door de runtime-opties van uw compiler. De openbare heap wordt tijdens runtime geïnitialiseerd met een grootteparameter.
2d) Wat maakt iemand sneller?
Ze zijn niet ontworpen om snel te zijn, ze zijn ontworpen om nuttig te zijn. Hoe de programmeur ze gebruikt, bepaalt of ze 'snel' of 'langzaam' zijn
REF:
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate
|
Veel antwoorden zijn correct als concept, maar we moeten er rekening mee houden dat de hardware (d.w.z. microprocessor) een stapel nodig heeft om subroutines aan te roepen (CALL in assembleertaal ..). (OOP jongens zullen het methoden noemen)
Op de stapel bewaar je retouradressen en call → push / ret → pop wordt direct in hardware beheerd.
Je kunt de stapel gebruiken om parameters door te geven ... zelfs als het langzamer is dan het gebruik van registers (zou een microprocessorgoeroe zeggen of een goed BIOS-boek uit de jaren 80 ...)
Zonder stack kan geen enkele microprocessor werken. (we kunnen ons geen programma voorstellen, zelfs niet in assembleertaal, zonder subroutines / functies)
Zonder de hoop kan het. (Een assembleertaalprogramma kan werken zonder, aangezien de heap een OS-concept is, als malloc, dat wil zeggen een OS / Lib-aanroep.
Het stapelgebruik is sneller als:
Is hardware en zelfs push / pop zeer efficiënt.
malloc vereist het openen van de kernelmodus, gebruik lock / semafoor (of andere synchronisatieprimitieven) om wat code uit te voeren en het beheren van enkele structuren die nodig zijn om de toewijzing bij te houden.
|
Heap is een gebied met dynamisch toegewezen geheugen dat automatisch wordt beheerd door het besturingssysteem of de geheugenbeheerbibliotheek. U kunt op elk moment een blok toewijzen en op elk moment vrijmaken. Heap-toewijzing vereist het bijhouden van een volledig record van welk geheugen is toegewezen en wat niet, evenals wat overheadonderhoud om fragmentatie te verminderen, aaneengesloten geheugensegmenten te vinden die groot genoeg zijn om in de gevraagde grootte te passen, enzovoort. Het geheugen kan op elk moment worden vrijgemaakt, waardoor er vrije ruimte overblijft. Naarmate de heap groeit, worden er vaak nieuwe blokken toegewezen van lagere adressen naar hogere adressen. U kunt de hoop dus zien als een hoop geheugenblokken die in omvang toenemen naarmate geheugen wordt toegewezen. Als de heap te klein is voor een toewijzing, kan de grootte vaak worden vergroot door meer geheugen van het onderliggende besturingssysteem te verkrijgen. Geheugen dat is toegewezen vanaf de heap, blijft toegewezen totdat een van de volgende situaties zich voordoet:
De herinnering is vrijgemaakt
Het programma wordt beëindigd
Stapel:
Net als de hoop opgeslagen in computer-RAM.
Variabelen die op de stapel zijn gemaakt, vallen buiten het bereik en worden automatisch ongedaan gemaakt.
Veel sneller toe te wijzen in vergelijking met variabelen op de heap.
Slaat lokale gegevens op, retouradressen, die worden gebruikt voor het doorgeven van parameters.
Kan een stack overflow hebben als er te veel van de stack wordt gebruikt (meestal
van oneindige of te diepe recursie, zeer grote toewijzingen).
U zou de stapel gebruiken als u precies weet hoeveel gegevens u nodig heeft
toewijzen vóór compilatietijd en het is niet te groot.
Meestal heeft een maximale grootte al bepaald bij uw programma
begint.
Hoop:
Net als de stapel opgeslagen in computer-RAM.
In C ++ moeten variabelen op de heap handmatig worden vernietigd en nooit
buiten het toepassingsgebied vallen.
De gegevens worden vrijgegeven met verwijderen, verwijderen [] of gratis.
Langzamer toe te wijzen in vergelijking met variabelen op de stapel.
Wordt op aanvraag gebruikt om een ​​gegevensblok toe te wijzen voor gebruik door het programma.
Kan fragmentatie hebben als er veel toewijzingen zijn en
deallocaties.
In C ++ of C wordt naar gegevens die op de heap zijn gemaakt, verwezen door pointers
en toegewezen met respectievelijk nieuw of malloc.
Kan toewijzingsfouten hebben als een te grote buffer wordt gevraagd
worden toegewezen.
Uzou de hoop gebruiken als u niet precies weet hoeveel gegevens u heeft
nodig heeft tijdens runtime of als u veel gegevens moet toewijzen.
Verantwoordelijk voor geheugenlekken.
|
De stapel is in wezen een gemakkelijk toegankelijk geheugen dat eenvoudig de items beheert
als een - nou ja - stapel. Alleen artikelen waarvan de maat vooraf bekend is, kunnen op de stapel. Dit is het geval voor getallen, strings, booleans.
De heap is een geheugen voor items waarvan u de
exacte maat en structuur. Omdat objecten en arrays kunnen worden gemuteerd en
veranderen tijdens runtime, ze moeten de hoop in gaan.
Bron: Academind
|
CPU-stack en heap zijn fysiek gerelateerd aan hoe CPU en registers werken met geheugen, hoe machine-assembleertaal werkt, niet op hoog niveau talen zelf, zelfs als deze talen kleine dingen kunnen beslissen.
Alle moderne CPU's werken met "dezelfde" microprocessortheorie: ze zijn allemaal gebaseerd op zogenaamde "registers" en sommige zijn bedoeld voor "stack" om betere prestaties te leveren. Alle CPU's hebben sinds het begin stapelregisters en ze waren er altijd geweest, bij wijze van spreken, zoals ik weet. Assembly-talen zijn hetzelfde sinds het begin, ondanks variaties ... tot aan Microsoft en zijn Intermediate Language (IL) die het paradigma veranderden om een ​​OO virtuele machine-assembleertaal te hebben. Dus we zullen in de toekomst wat CLI / CIL CPU kunnen hebben (één project van MS).
CPU's hebben stapelregisters om geheugentoegang te versnellen, maar ze zijn beperkt in vergelijking met het gebruik van andere registers om volledige toegang te krijgen tot al het beschikbare geheugen voor de processus. Daarom hebben we het gehad over stack- en heap-toewijzingen.
Samengevat, en in het algemeen, is de heap hudge en traag en is deze voor "globale" instanties en inhoud van objecten, aangezien de stapel klein en snel is en voor "lokale" variabelen en verwijzingen (verborgen aanwijzingen om ze te vergeten te beheren).
Dus als we het nieuwe trefwoord in een methode gebruiken, wordt de referentie (een int) gemaakt in de stapel, maar het object en al zijn inhoud (waardetypes en objecten) wordt in de heap gemaakt, als ik me goed herinner. Maar lokale elementaire waardetypen en arrays worden in de stapel gemaakt.
Het verschil in geheugentoegang zit hem op het niveau van de celreferenties: het adresseren van de heap, het totale geheugen van het proces, vereist meer complexiteit in termen van het verwerken van CPU-registers dan de stack die lokaal 'meer' is in termen van adressering omdat de register wordt gebruikt als basisadres, als ik het me herinner.
Het is de reden waarom wanneer we zeer lange of oneindige herroepingsoproepen of -lussen hebben, we snel een stack overflow hebben, zonder het systeem op moderne computers te bevriezen ...
C # Heap (ing) versus Stack (ing) in .NET
Stack vs Heap: ken het verschil
Statische klasse geheugentoewijzing waar het is opgeslagen C #
Wat en waar zijn de stapel en hoop?
https://en.wikipedia.org/wiki/Memory_management
https://en.wikipedia.org/wiki/Stack_register
Bronnen voor assembleertaal:
Zelfstudie voor assemblageprogrammering
Intel® 64 en IA-32 Architectures Software Developer Handleidingen
|
Bedankt voor een heel goede discussie, maar als echte noob vraag ik me af waar de instructies worden bewaard? In de BEGINNING waren wetenschappers aan het beslissen tussen twee architecturen (von NEUMANN waar alles wordt beschouwd als DATA en HARVARD, waar een geheugengebied was gereserveerd voor instructies en een ander voor gegevens). Uiteindelijk gingen we voor het ontwerp van von Neumann en nu wordt alles als 'hetzelfde' beschouwd. Dit maakte het moeilijk voor mij toen ik de vergadering leerde
https://www.cs.virginia.edu/~evans/cs216/guides/x86.html
omdat ze praten over registers en stapelwijzers.
Alles hierboven heeft het over DATA. Mijn gok is dat aangezien een instructie een gedefinieerd ding is met een specifieke geheugenvoetafdruk, het op de stapel zou gaan en dus al 'die' registers die tijdens de montage worden besproken, op de stapel staan. Toen kwam natuurlijk objectgeoriënteerd programmeren met instructies en gegevens die in een structuur kwamen die dynamisch was, dus nu zouden de instructies ook op de hoop worden bewaard?
|
Zeer actieve vraag. Verdien 10 reputatie om deze vraag te beantwoorden. De reputatievereiste helpt deze vraag te beschermen tegen spam en niet-beantwoording.
Niet het antwoord waar je naar zoekt? Blader door andere vragen met de tag geheugenbeheerstack taal-agnostische heap dynamische geheugentoewijzing of stel uw eigen vraag.